package state


// import "fmt"


// 返回当前PC
func (self *luaState) PC() int {

	return self.stack.pc
}


// 修改PC(用于实现跳转指令)
func (self *luaState) AddPC(n int) {

	self.stack.pc += n
}


// 根据PC索引从函数原型的指令表里取出当前指令，然后把PC+1
// 这样下次再调用该方法取出的就是下一条指令
func (self *luaState) Fetch() uint32 {

	i := self.stack.closure.proto.Code[self.PC()]
	self.AddPC(1)

	return i
}


// 根据索引从函数原型的常量表里取出一个常量值，然后把它推入栈顶
func (self *luaState) GetConst(idx int) {

	c := self.stack.closure.proto.Constants[idx]

	self.stack.push(c)
}


// 根据情况调用GetConst() 方法把某个常量推入栈顶
// 或者调用PushValue()方法把某个索引处的栈值推入栈顶
// 传递给GetRK()方法的参数实际上是iABC模式指令里的OpArgK类型参数
// 这种类型的参数一共占9个比特
// 如果最高位为1，那么参数里存放的是常量表索引，把最高位去掉就可以得到索引值
// 否则最高位是0，参数里存放的就是寄存器索引值
// 注意!!!
// Lua虚拟机指令操作数里携带的寄存器索引是从0开始的
// 而LuaAPI里的栈索引是从1开始的，所以当需要把寄存器索引当成栈索引使用时，要对寄存器的索引+1
func (self *luaState) GetRK(rk int) {

	if rk > 0xFF {	// constant
		self.GetConst(rk & 0xFF)
	} else {	// register
		self.PushValue(rk+1)
	}
}


// 返回当前Lua函数所操作的寄存器数量
func (self *luaState) RegisterCount() int {

	return int(self.stack.closure.proto.MaxStackSize)
}


// 把传递给当前Lua函数的变长参数推入栈顶(多退少补)
func (self *luaState) LoadVararg(n int) {

	if n < 0 {
		n = len(self.stack.varargs)
	}

	self.stack.check(n)
	self.stack.pushN(self.stack.varargs, n)
}


// 把当前Lua函数的子函数的原型实例化为闭包推入栈顶
func (self *luaState) LoadProto(idx int) {

	stack := self.stack

	proto := stack.closure.proto.Protos[idx]
	closure := newLuaClosure(proto)

	stack.push(closure)

	for i, uvInfo := range proto.Upvalues {

		uvIdx := int(uvInfo.Idx)

		if uvInfo.Instack == 1 {  // (子闭包的)Upvalue捕获的是当前函数的局部变量

			// 如果(子闭包的)Upvalue捕获的外围函数局部变量还在栈上
			// 直接引用即可
			// 我们称这种Upvalue处于开放状态(Open)
			// 反之，必须把变量的实际值保存到其它地方
			// 我们称这种Upvalue处于闭合(Closed)状态

			if stack.openuvs == nil {

				stack.openuvs = map[int]*upvalue{}

			}

			if openuv, found := stack.openuvs[uvIdx]; found {

				closure.upvals[i] = openuv

			} else {

				closure.upvals[i] = &upvalue{&stack.slots[uvIdx]}
				stack.openuvs[uvIdx] = closure.upvals[i]

			}

		} else {	// (子闭包的)Upvalue捕获的是更外围的函数中的局部变量
			// 该Upvalue已经被当前函数捕获
			// 我们只要把该Upvalues传递个闭包即可

			closure.upvals[i] = stack.closure.upvals[uvIdx]

		}

	}

}


// 处于开启状态的Upvalue引用了还在寄存器的Lua值
// 我们把这些值从寄存器里复制出来，然后更新Upvalue
// 这样就将其改为了闭合状态
func (self *luaState) CloseUpvalues(a int) {

	for i, openuv := range self.stack.openuvs {

		if i >= a - 1 {
			val := *openuv.val  // openuv.val指向寄存器中的内容, val值拷贝这份内容
			openuv.val = &val   // 将值拷贝的结果赋值给openuv.val，这样寄存器对应的栈被回收后，结果保持正确
			delete(self.stack.openuvs, i)  // 从self.stack.openuvs删除这个openuv

		}

	}

}